package com.groupwork.resourcemonitoruserserver.shiro;

import com.alibaba.nacos.common.utils.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.groupwork.resourcemonitorcommon.utils.JWTToken;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JWTFilter extends AccessControlFilter {

    /**
     * RefreshToken过期时间(秒)
     */
//    @Value("${config.refresh-token-expire-time}")
//    private long refreshTokenExpireTime;

    /**
     * accessToken key
     */
    private final String ACCESS_TOKEN_KEY = "Authorization";

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        System.out.println("登录拦截");
        String accessToken = getAccessToken(request);
        if (StringUtils.isBlank(accessToken)) {
            return false;
        }
        try {
            System.out.println("进入登录");
            getSubject(request, response).login(new JWTToken(accessToken));
        } catch (Exception ex) {
            System.out.println(ex);
            //如果token过期则尝试刷新token
            //暂时不提供刷新
//            if (throwable instanceof TokenExpiredException) {
//                return this.refreshToken(accessToken, request, response);
//            }
            return false;
        }
        return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        System.out.println("登录失败");
        HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse);
        ObjectMapper mapper = new ObjectMapper();
//        String responseBody = mapper.writeValueAsString(ResponseEntity.error(ErrorCode.AUTH_FAILED));
//        ServletUtil.write(httpResponse, responseBody, "application/json; charset=utf-8");
        return false;
    }


    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        httpServletResponse.setHeader("Access-Control-Expose-Headers", ACCESS_TOKEN_KEY);
        httpServletResponse.setHeader("Cache-Control", "no-store");
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 刷新token,该方法为同步方法同一accessToken在同一时间只能调用一次，防止重复刷新token
     *
     * @return 是否刷新成功
     */
//    private boolean refreshToken(String accessToken, ServletRequest request, ServletResponse response) {
//        //加锁，保障同一token在同一时间只能刷新一次，避免重复刷新
//        String lockKey = LockKeys.LOCK_REFRESH_TOKEN_PREFIX + accessToken;
//        try {
//            LockUtils.lock(lockKey);
//            if (RedisUtils.hasKey(RedisKeys.AUTH_EXPIRING_TOKEN + accessToken)) {
//                String newToken = (String) RedisUtils.get(RedisKeys.AUTH_EXPIRING_TOKEN + accessToken);
//                // 提交给Realm进行认证
//                try {
//                    this.getSubject(request, response).login(new JWTToken(newToken));
//                } catch (Exception ex) {
//                    Console.log(ex.getMessage());
//                    return false;
//                }
//                return true;
//            } else {
//                //获取用户id
//                String subjectId = JWTUtils.getSubjectId(accessToken);
//                String timestamp = JWTUtils.getTimestamp(accessToken);
//                String jwtId = JWTUtils.getId(accessToken);
//                boolean isAdmin = JWTUtils.isAdmin(accessToken);
//                if (RedisUtils.hasKey(RedisKeys.AUTH_REFRESH_TOKEN + jwtId)) {
//                    String currentTimeMillisRedis = (String) RedisUtils.get(RedisKeys.AUTH_REFRESH_TOKEN + jwtId);
//                    // 获取当前AccessToken中的时间戳，与RefreshToken的时间戳对比，如果当前时间戳一致，进行AccessToken刷新
//                    if (StringUtils.equals(currentTimeMillisRedis, timestamp)) {
//                        // 生成新的jwt标识
//                        String newJwtId = UUID.randomUUID().toString(true);
//                        // 获取当前最新时间戳
//                        String currentTimeMillis = String.valueOf(System.currentTimeMillis());
//                        // 设置RefreshToken中的时间戳为当前最新时间戳并重置过期时间
//                        RedisUtils.set(RedisKeys.AUTH_REFRESH_TOKEN + newJwtId, currentTimeMillis, refreshTokenExpireTime);
//                        // 刷新AccessToken，设置时间戳为当前最新时间戳
//                        String newAccessToken = JWTUtils.generate(newJwtId, subjectId, isAdmin, currentTimeMillis);
//                        // 提交给Realm进行认证
//                        try {
//                            this.getSubject(request, response).login(new JWTToken(newAccessToken));
//                        } catch (Exception ex) {
//                            Console.log(ex.getMessage());
//                            return false;
//                        }
//                        // 最后将刷新的AccessToken存放在Response的Header中的Authorization字段返回
//                        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//                        httpServletResponse.setHeader(ACCESS_TOKEN_KEY, newAccessToken);
//                        // 设置旧token设置为即将过期,30秒
//                        RedisUtils.set(RedisKeys.AUTH_EXPIRING_TOKEN + accessToken, newAccessToken, 30);
//                        // 删除旧的refresh token
//                        RedisUtils.del(RedisKeys.AUTH_REFRESH_TOKEN + jwtId);
//                        return true;
//                    }
//                }
//            }
//
//        } finally {
//            LockUtils.unlock(lockKey);
//        }
//        return false;
//    }

    /**
     * 获取AccessToken
     *
     * @param request request
     * @return AccessToken
     */
    private String getAccessToken(ServletRequest request) {
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        String accessToken = httpRequest.getHeader(ACCESS_TOKEN_KEY);
        System.out.println(accessToken);
        if (StringUtils.isBlank(accessToken)) {
            accessToken = httpRequest.getParameter(ACCESS_TOKEN_KEY);
        }
        return accessToken;
    }
}
